#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>

#include "json_request.h"

struct MemoryStruct {
  char *memory;
  size_t size;
};

static boolean initialized = FALSE;

void json_initialize() {
  if (!initialized) {
    curl_global_init(CURL_GLOBAL_ALL);
    initialized = TRUE;
  }
}

void json_deinitialize() {
  if (initialized) {
	curl_global_cleanup();
	initialized = FALSE;
  }
}


static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
  size_t realsize = size * nmemb;
  struct MemoryStruct *mem = (struct MemoryStruct *)data;

  mem->memory = realloc(mem->memory, mem->size + realsize + 1);
  if (mem->memory == NULL) {
    /* out of memory! */
    printf("not enough memory (realloc returned NULL)\n");
    exit(EXIT_FAILURE);
  }

  memcpy(&(mem->memory[mem->size]), ptr, realsize);
  mem->size += realsize;
  mem->memory[mem->size] = 0;

  return realsize;
}

struct json_response_t json_http_request(const char *url, char *params) {
  CURL *curl_handle;
  struct MemoryStruct chunk;
  json_object *json_response;
  json_object *json_statuscode;
  struct json_response_t response;

  chunk.memory = malloc(1);
  chunk.size = 0;

  curl_handle = curl_easy_init();
  curl_easy_setopt(curl_handle, CURLOPT_URL, url);
  curl_easy_setopt(curl_handle, CURLOPT_POST, 1);
  curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, params);
  curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
  curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
  curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "jabberd2-json/0.31");

  curl_easy_perform(curl_handle);

  curl_easy_cleanup(curl_handle);

  response.status = -1;
  response.json = NULL;

  if (chunk.size > 0 && chunk.memory) {
	json_response = json_tokener_parse(chunk.memory);

	free(chunk.memory);
    chunk.memory = NULL;

	if (json_response) {
	  json_statuscode = json_object_object_get(json_response, "statusCode");
	  if (json_statuscode) {
	    response.status = json_object_get_int(json_statuscode);
        response.json = json_response;
	  }
	}

	return response;
  }

  if (chunk.memory) {
    free(chunk.memory);
  }

  return response;
};

void free_json_response_object(json_object *object) {
  if (object == NULL)
	return;

  json_type object_type;
  struct json_object *value;
  struct lh_entry *entry;
  int array_len;
  int i;

  object_type = json_object_get_type(object);

  switch (object_type) {
    case json_type_array: {
      array_len = json_object_array_length(object);
      for (i = 0; i < array_len; i++) {
        free_json_response_object(json_object_array_get_idx(object, i));
      }
    }
    break;
    case json_type_object: {
      entry = json_object_get_object(object)->head;
      while (entry) {
    	value = (struct json_object*)entry->v;
    	free_json_response_object(value);
    	entry = entry->next;
      }
    }
    break;
    case json_type_null:
      return;
    break;
  }

  json_object_put(object);
}

void free_json_response(struct json_response_t response) {
  free_json_response_object(response.json);
  response.json = NULL;
}

char *get_url(const char *protocol, const char *host, const char *port, const char *path) {
  char *url;

  url = malloc(4 + strlen(protocol) + strlen(host) + strlen(port) + strlen(path));
  sprintf(url, "%s://%s:%s%s", protocol, host, port, path);

  return url;
}
